<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }

        a,a:active,a:hover{
            text-decoration: none;
        }

        ul,ol,li{
            list-style: none;
        }

        img{
            width: 100%;
            height: 100%;
        }

        .clear{
            clear: both;
            content: "";
            display: block;
        }

        .banner{
            width: 800px;
            height: 400px;
            border: solid;
            margin: 40px auto;
            position: relative;
        }

        .banner > ul{
            width: 500%;
            height: 100%;
            position:relative;
        }

        .banner > ul > li{
            width: 800px;
            height: 100%;
            color: #fff;
            display: flex;
            justify-content: center;
            align-items: center;
            float:left;
            font-size: 150px;
        }

        .banner > ol {
            height: 50px;
            padding: 0px 10px ;
            background: rgba(0,0,0,.5);
            position: absolute;
            bottom: 50px;
            left: 50%;
            transform:translateX(-50%) ;
            border-radius: 15px ;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .banner > ol > li{
            width: 20px;
            height: 20px;
            border-radius: 50%;
            background: #fff;
            margin: 0 15px;
            cursor: pointer;
        }

        .banner > ol > li.active{
            background: red;
        }

        .banner > div{
            width: 100%;
            height: 50px;
            position: absolute;
            left: 0;
            top: 50%;
            transform:translateY(-50%) ;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .banner > div > a{
            display: flex;
            width: 50px;
            height: 50px;
            font-size: 30px;
            color: #fff;
            background: rgba(0,0,0,.5);
            justify-content: center;
            align-items: center;
        }
    </style>
</head>
<body>
    
    <div class="banner">
        <!-- 实际项目中,图片标签,都是动态生成的 -->
        <ul class="clear"></ul>
        <ol></ol>
        <div>
            <a name="left" href="JavaScript:;">&lt;</a>
            <a name="right" href="JavaScript:;">&gt;</a>
        </div>
    </div>

    <script src="../../../tools.js"></script>
    <script>
        // 目标轮播图,原始图片,路径信息,应该是 img标签 src的属性值
        var imgArr = [
            { width:1920,height:500,size:127,src:'./images/1.jpg',name:'轮播图1' },
            { width:1920,height:500,size:337,src:'./images/2.jpg',name:'轮播图2' },
            { width:1920,height:500,size:247,src:'./images/3.jpg',name:'轮播图3' },

        ];



        // 获取标签对象:

        // 获取轮播图div标签对象
        const oBanner = document.querySelector('.banner');

        // 获取轮播图div中的ul标签对象
        const oUl = oBanner.querySelector('ul');

        // 获取轮播图div中的ol标签对象
        const oOl = oBanner.querySelector('Ol');


        // 定义变量

        // 存储数组单元格数
        let arrLength = imgArr.length;

        // 获取div宽度或者获取li宽度
        let divWidth = parseInt( myGetStyle( oBanner , 'width' ) );

        // 定义变量,储存 轮播图 轮序播放时 对应的li 的索引下标
        // 起始值 是 原始轮播图的 第一张图片 也就是 当前li中的第二个li 索引下标是 1
        let index = 1;

        // 定义变量,准备存储定时器编号
        // 不需要赋值,生成定时器时 再赋值
        let time;
 
        // 1, 调用函数, 根据数组数据库, 生成 ul和ol中的标签对象
        setLi(imgArr);

        // 2, 调用自动轮播函数,让轮播图,自动轮序播放
        autoLoop();

        // 3, 鼠标的移入移出事件
        mouseEvent()

        // 4, 点击事件
        setClick()

        // 5, 浏览器隐藏函数 
        hid()




        // 一, 定义一个函数,根据数组属性,生成 标签字符串,吸入ul,ol
        function setLi(array){
            // 根据数组,生成 字符串标签

            // 存储 生成的ul>li标签对象字符串
            let ulStr = '';
            // 存储 生成的ol>li标签对象字符串
            let olStr = '';

            // 循环遍历数组对象
            array.forEach(function(val,key){
                // ul>li中 标签内容是 li>img>src属性值是数组中存储的路径数据
                ulStr += `
                    <li>
                        <img src="${val.src}">    
                    </li>
                `
                // ol>li中 标签内容是 li标签本身
                // 第一个li标签,也就是索引是0时,拼接的li,添加class样式
                // 其他li标签,没有 class样式
                olStr += key === 0 ?  `<li name="focus" num="${key}" class="active"></li>` : `<li num="${key}" name="focus" ></li>`;
                
            })

            // 将生成的ul>li字符串写入ul标签对象
            oUl.innerHTML = ulStr;
            // 将生成的ol>li字符串写入ol标签对象
            oOl.innerHTML = olStr;

            // 将原始轮播图写入到ul中之后,ul中才有li标签
            // 将原始第一个li,复制克隆,写入到ul标签的末位
            // 将原始最后一个li,复制克隆,写入到ul标签的首位
            
            // 获取ul中所有的li标签对象
            const oOlLis = oUl.querySelectorAll('li'); 

            // 获取第一个li标签对象和最后一个li标签对象
            const oFirst = oOlLis[0];
            const oLast = oOlLis[arrLength-1];

            // 克隆第一个和最后一个标签对象 
            const oFirstClone = oFirst.cloneNode(true);
            const oLastClone = oLast.cloneNode(true);

            // 克隆的第一个标签,写入到ul的最后
            oUl.appendChild( oFirstClone );
            // 克隆的最后一个标签,写入到ul的起始
            oUl.insertBefore( oLastClone ,  oFirst);


            // 设定 oUl的宽度 是 一个li的宽度/div宽度 * li的个数+2
            // 也就是数组中数据单元个数也就是轮播图原始图片个数 + 2张克隆的图片个数
            oUl.style.width = divWidth * (arrLength+2) + 'px';

            // 重新定位 ul 
            // 定位的数值是 负的 index * li/div宽度 拼接 px单位
            oUl.style.left = -index * divWidth + 'px';

            // oUl.style.left = `${-index * divWidth}px`;

        }

        // 2 自动轮播函数
        function autoLoop(){
            // 定义一个定时器
            // 将 定时器的 序号 赋值给 变量 存储
            time = setInterval(function(){
                // 1, 每次执行定时器,先给 index 执行自增操作
                index++;

                // 2, 调用 move运动函数
                // 运动的标签对象,是 ul 标签
                // 运动的属性和属性值
                //     属性是 left 属性
                //     属性值 负的 index 乘以 一个li/div的宽度
                // 参数3,是定义的 运动结束回调函数的 函数名称
                move(oUl , {left: -index*divWidth} , loopEnd)

            } , 3000)
        }


        // 3, 运动终止时,调用的回调函数
        function loopEnd(){
            // 判断 index 中 存储的数值

            // 如果 index值 是 最后一个li的索引下标
            // 也就是 原始数组的长度 +2 -1 
            if( index === arrLength+2-1 ){
                // 切换 至 整数第二个li标签对象 
                index = 1;
            }

            // 如果 index值 是 第一个li的索引下标
            if( index === 0 ){
                // 切换至 倒数 第二个 li标签对象
                index = arrLength+2-1-1
            }

            // 根据 新的 index 数值 对 ul标签,重新定位
            // 定位是 瞬间改变的 不是调用move运动函数,动画完成的
            oUl.style.left = -index * divWidth + 'px';

            // 调用 设定 焦点按钮 class样式函数
            setFocusStyle()

        }

        // 4, 切换 ol>li 焦点按钮 class样式
        function setFocusStyle(){
            // 获取所有的ol>li标签对象
            // 必须注意,ol>li标签对象,是动态生成的
            // 必须要在写入之后,再获取
            const oOiLis = oOl.querySelectorAll('li');

            // 循环遍历 oOiLis 中所有的 ol>li 标签
            oOiLis.forEach(function(val){
                // 先清除所有ol>li,class中,active属性值
                myDelClass( val , 'active' );
            })

            // 给当前ul>li对应的 ol>li 添加 class,active样式
            // 对应 关系是 ul>li 的索引下标-1 是 ol>li的索引下标
            // 当前 ul>li 多索引下标 也就是 index的数值
            // ol>li的索引下标 就是 index-1
            oOiLis[index-1].className += ' active'
        }


        // 5, 鼠标移出移出事件
        // 给 div 添加 移入事件 使用 mouseenter 子级标签不触发
        // 给 div 添加 移出事件 使用 mouseleave 子级标签不触发
        function mouseEvent(){
            oBanner.addEventListener( 'mouseenter' , function(){
                // 鼠标移入div,清除定时器
                clearInterval(time)
            })

            oBanner.addEventListener( 'mouseleave' , function(){
                // 鼠标移出div,再次启动自动轮播函数
                autoLoop();
            })
        }


        // 6, 点击事件,点击焦点 和 左右切换按钮
        function setClick(){
            // 给 父级div 标签添加点击事件
            oBanner.addEventListener( 'click' , function(e){

                // 如果点击对象 e.target name属性值 是 left 
                // 点击的是 左切换
                if( e.target.getAttribute('name') === 'left' ){
                    // 点击左切换,显示下一个li,index++
                    index++;

                    // 调用move函数
                    // 根据新的index数值
                    // 使用move运动函数
                    // 运动完成图片的切换
                    move(oUl , {left: -index*divWidth} , loopEnd);
                }

                // 如果点击对象 e.target name属性值 是 right 
                // 点击的是 右切换
                if( e.target.getAttribute('name') === 'right' ){
                    // 点击左切换,显示下一个li,index++
                    index--;

                    // 调用move函数
                    // 根据新的index数值
                    // 使用move运动函数
                    // 运动完成图片的切换
                    move(oUl , {left: -index*divWidth} , loopEnd);
                }


                // 如果点击对象 e.target name属性值 是 focus 
                // 点击的是 焦点切换
                if( e.target.getAttribute('name') === 'focus' ){
                    
                    // index 的数值是 标签中num属性的属性值+1
                    // 任意属性的属性值一定是字符串类型,必须要先转化为数值类型 -0 *1 /1
                    index = e.target.getAttribute('num')-0 + 1;

                    // 调用move函数
                    // 根据新的index数值
                    // 使用move运动函数
                    // 运动完成图片的切换
                    move(oUl , {left: -index*divWidth} , loopEnd);
                }

            } )
        }

        // 7, 浏览器隐藏函数
        function hid(){
            // 当前浏览器窗口显示状态事件
            // 也就是 浏览器的显示状态改变时会触发程序
            document.addEventListener( 'visibilitychange' , function(){

                // 如果显示状态是 visible 也就是 当前窗口是显示状态
                if(document.visibilityState === 'visible'){
                    // 调用 自动轮播函数 启动轮播程序
                    autoLoop();
                }
                // 如果显示状态是 hidden 也就是 当前窗口是隐藏状态
                if(document.visibilityState === 'hidden'){
                    // 清除定时器,停止自动轮播的执行
                    clearInterval(time);
                }
            })
        }



        /* 
            
            轮播图1 HTML css定义

            轮播图2 生成 ul中li和img图片标签 ol中li标签
                2-1 轮播图 原始图片 6张 ul,ol中都是生成6个li 
                    1, 生成 ul>li 和 ol>li 标签对象
                    循环变量 数组 
                            ul>li 内容是: 
                                li标签中嵌套 img标签 
                                img标签 scr 属性值 是 数组中存储的路径信息,实际项目中一般是一个图片的网址

                            ol>li 内容是:
                                第一个li标签,拼接class样式
                                li标签本身

                    2, 将 生成的 ul>li 字符串, 写入 ul标签对象 
                    将 生成的 ol>li 字符串, 写入 ol标签对象
                    
                    3, 设定 动态渲染生成 ul宽度
                        div宽度/li宽度 * 数组中单元个数 + 2
                                        li个数,轮播图原始图片个数 + 两个克隆的图片个数

        
            轮播图3 自动轮播
                1, 本质:
                    所谓的轮播运动,本质是改变 标签ul 定位属性left 的属性值
                    属性值的改变,不是 直接完成的 是通过 运动函数 完成的动画效果

                    move( 运动的标签对象 , 运动的属性和属性值 , 运动结束执行的回调函数 )
                        运动的标签对象是 ul标签

                        运动的属性和属性值 运动的属性是 定位的left 属性
                                         属性值是 显示的li标签,索引下标 * 一个li/一个div 的宽度

                        运动结束执行的回调函数 是 后面定义的 loopEnd 函数
                                                赋值时 要 赋值 函数名称

                    定义一个变量,变量储存 轮播时 循环到 那个li的索引下标
                    起始 时 显示的是 原始轮播图,第一张图片,但是 是 现在 所有li中的 第二个li 索引下标是 1

                    通过定时器 每间隔一段时间 给 变量index++
                    定位的 属性值 就是   负的 index * li/div宽度 
                                        -index * divWidth 

                2, 关键变量index 
                    index变量中 存储的数值 本质是 索引下标
                    是 当前显示的 li标签的对应的 索引下标
                    显示的li标签 是通过 调整 ul定位 实现的
                    索引下标也就是对应的 定位left 的数值 
                    定位left的数值 本质上 是 -li的索引下标 * 一个li/div的宽度

                    轮播图中,使用index变量储存的数值,控制 ul定位 也就是 控制 轮播图显示的图片
        
        
        
            轮播图4: 运动停止,回调函数

                1, 显示图片的切换  也就是 ul定位的改变 也就是 index的改变
                    如果 index 是 最后一个li的索引下标 
                        运动结束也就是 ul定位结束 也就是显示图片切换结束
                            ul定位 应该是 第二个li的定位 
                            也就是 index 要 赋值 初始状态的 1
                            给 ul 重新定位 定位数值还是 -index * 一个li/div的宽度
                            这个 切换回 第二个li显示 需要瞬间完成 不要运动完成
                            直接定位,不是调用 运动函数

                2, 切换 焦点li--ol>li 标签样式
                    方法和思路 和 tab切换/选项卡类似
                    清除 所有 ol>li 标签 class,active样式
                    根据 当前 显示li的索引下标 也就是 index变量中存储的 数值
                    找到 对应的 ol>li 标签 添加 class,active 样式
                    对应关系 ul>li的索引下标 -1  对应  ol>li 索引下标


            轮播图5: 鼠标的移入移出事件
                鼠标移入 停止定时器
                鼠标移出 再次启动 自动轮播函数

                移入移出事件最好使用 mouseenter 和 mouseleave 事件 
                确保 子级标签 不会触发

                使用 let 定义储存定时器变量 定义为全局作用域变量
                在 自动轮播函数中 生成 定时器时 对 这个变量 只 做 赋值 操作

            轮播图6: 鼠标的点击事件
                点击 左右切换 按钮 点击 焦点按钮
                需要给标签添加不同的 name 区分点击对象
                name 是 left  点击的是 左切换按钮
                name 是 right 点击的是 右切换按钮
                name 是 focus 点击的是 焦点切换按钮

                通过 事件委托方式 给 父级标签 div 添加点击事件
                通过 点击对象 name 的属性值 判断 点击的是谁 执行不同的程序

                点击左切换 显示下一个li的内容 index++
                之后调用move函数

                点击右切换 显示上一个li的内容 index--
                之后调用move函数

                如果切换至 第一个 li 也就是 index 为 0 时 
                需要重新定位ul标签位置,切换至 倒数第二个li显示
                切换是 瞬间完成的 不是 move运动完成的
                在 运动终止函数 loopEnd 中 添加对 index的判断

                点击焦点切换  显示 点击焦点也就是ol>li 对应的 ul>li
                ul>li 和 ol>li 的对应关系 :  
                    ul>li的index - 1 是 ol>li的index
                    ol>li的index + 1 是 ul>li的index 
                点击 ol>li 获取 点击 li标签的 索引下标
                回去结果+1 就是 对应 ul>li的索引下标 
                将结果赋值 给 index 根据新的 index 调用执行 move函数

                生成 ol>li 时 添加 属性 熟悉值是 索引下标

                提示: move运动函数,不要写在 if判断之外
                     如果写在if判断之外 每次点击 div标签,都会被触发
                     只是index 数值没有改变 所以没有运动效果

                     写在 if判断内 只有点击 左右切换 和 焦点切换 才会触发


            轮播图7 浏览器隐藏
                浏览器隐藏时 会 造成的问题
                    当浏览器隐藏 或者 切换到其他窗口显示时
                    被隐藏的浏览器窗口 就不会执行 程序
                    但是 定时器 会继续执行
                    一旦切换回 显示状态 多个 定时器 会同时执行
                    但是每个定时器 运动属性相同,但是属性值不同
                    就会造成页面显示混乱

                解决方案:
                    浏览器窗口被隐藏, 清除定时器

                    浏览器窗口显示, 就再次启动 自动轮播函数

                        visibilitychange    是 浏览器状态改变事件
                                            当 浏览器状态改变时触发事件执行

                        visibilityState     浏览器显示状态
                                            属性值是 visible 表示 当前浏览器窗口显示
                                            属性值是 hidden  表示 当前浏览器窗口隐藏






        */
    </script>
</body>
</html>